package com.example.threadcoroutine

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 *  协程域  CoroutineScope      ==   Thread
 *        挂起的函数代码块必须在CoroutineScope里执行
 *  suspend修饰的函数/方法    挂起函数  这个函数耗时比较长 不希望阻塞主线程
 *         挂起函数的执行必须在CoroutineScope内执行
 *  协程上下文 CoroutineContext  这个协程在哪个线程上执行
 *        Dispatchers.IO   子线程
 *        Dispatchers.Main UI主线程
 *        Dispatchers.Default 默认
 *
 *   使用launch开启一个同步任务 return: Job     a -> b -> c
 *   使用async开启一个异步任务 return: Deferred()   a  b  c  同时做
 *      await() 获取对应的值
 *
 *   使用withContext()  切换线程
 *        Dispatchers.IO   子线程
 *        Dispatchers.Main UI主线程
 *
 *    Flow 数据流
 *       当调用一个方法，在不同的时间/情况可能需要返回多个值 (List,  高阶函数）
 *        - emit()   发射新的数据
 *        - collect()  订阅/接收数据
 *        - flow{}    创建一个Flow对象
 *
 * Activity/Fragment
 *    -lifecycleScope : Activity/Fragment为我们默认提供了一个CoroutineScope 直接使用就可以了
 *          如果是自己创建，自己需要管理这个协程的生命周期 （创建->销毁）
 * ViewModel
 *    -viewModelScope
 * 除此以外 就需要自己创建CoroutineScope
 */
class SecondActivity : AppCompatActivity() {
    lateinit var mTextView: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mTextView = findViewById(R.id.textView)
        findViewById<Button>(R.id.button).setOnClickListener {
            //创建一个协程域
//            val scope = CoroutineScope(Dispatchers.Main)
            //在这个协程域上开启一个任务
//            val job1 = scope.launch(Dispatchers.IO){ //任务默认就在scope所在的区域执行
//                count1()
//            }

//            val job2 = scope.launch(Dispatchers.IO){
//                count2()
//            }

//           val sum1 =  scope.async { //开启一个异步任务
//                var sum = 0
//                for (i in 1..100){
//                    sum += i
//                }
//                sum
//            }
//
//            val sum2 =  scope.async { //开启一个异步任务
//                var sum = sum1.await()
//                for (i in 1..100){
//                    sum += i
//                }
//                sum
//            }
//
//            scope.launch {
//                mTextView.text = "${sum2.await()}"
//            }
//            CoroutineScope(Dispatchers.Main).launch {
//               val gunFlow =  loadDatas()
//                //接收反射出来的子弹(订阅）
//                gunFlow.collect {
//                    mTextView.text = "$it"
//                }
//            }


        }

//        lifecycleScope.launch(Dispatchers.IO){
//
//        }

        lifecycleScope.launch {
            val viewModel = MyViewModel()
            val dataFlow = viewModel.loadData()
            dataFlow.collect{
                //....
                mTextView.text = "$it"
            }
        }
    }

    suspend fun count1(){
        for (i in 1..5){
            withContext(Dispatchers.Main){//切换上下文  切换代码执行的scope 切换线程
                mTextView.text = "$i"
            }
            delay(500)
        }
    }
    suspend fun count2(){
        for (i in 11..15){
            withContext(Dispatchers.Main){//切换上下文  切换代码执行的scope 切换线程
                mTextView.text = "$i"
            }
            delay(1000)
        }
    }

    //这个方法是在IO线程执行
    suspend fun  loadDataFromDB():Int = withContext(Dispatchers.IO){
        //.......
        10
    }

    //Flow == 枪  （枪里面有很多子弹  但是同一时刻 只能打出来一颗  通过不断开枪的方式 把所有的子弹打出来）外部可以接收到我的子弹
    suspend fun loadDatas(): Flow<Int> = withContext(Dispatchers.IO){
        //.....  1 2 3 4 5 6 7 8 10
        //将子弹放到枪里面   直接开枪
       flow {
           for (i in 1..10){
               emit(i) //发射
               delay(500)
           }
       }
    }
}























